/* eslint-disable max-classes-per-file */
import { T_iconFont } from '@/config/iconFont.config';
import * as ts from '@/obj/class';
import * as ifs from '@/obj/ifs';
import * as tvs from '@/obj/typeValue';
import * as vuePropertyDecorator from 'vue-property-decorator';
import * as vuexClass from 'vuex-class';
import { modal } from 'vxe-table';
import { ModalController } from 'vxe-table/types';
// import * as DEC from './decorator';
import { i18n, langContext, VueI18n } from '@/config/i18n.config';
import { I_wsUserMsg, T_sendWsMsgParams } from '@/obj/class/arg/msg';
import * as types from '@/types';
import Vue from 'vue';
import { updI18nLastActivityTime } from './deftask';
import { T_errDet } from './errDetail';
import httpReqV1 from './httpRequest';
import { T_lockInfo } from './lock';
import { I_msgHandler, T_msgHandler } from './msgHandler';
import {
  I_personalPreference,
  T_personalPreference
} from './PersonalPreference';

type T_httpReqV1 = typeof httpReqV1;

/*
全局服务类
*/
class T_srv extends ts.T_data {
  static vue = vuePropertyDecorator.Vue;

  static comp = vuePropertyDecorator.Component;

  static mixins = vuePropertyDecorator.Mixins;

  static prop = vuePropertyDecorator.Prop;

  static watch = vuePropertyDecorator.Watch;

  static provide = vuePropertyDecorator.Provide;

  static inject = vuePropertyDecorator.Inject;

  static injectReactive = vuePropertyDecorator.InjectReactive;

  static provideReactive = vuePropertyDecorator.ProvideReactive;

  static emit = vuePropertyDecorator.Emit;

  static ref = vuePropertyDecorator.Ref;

  static model = vuePropertyDecorator.Model;

  static vModel = vuePropertyDecorator.VModel;

  static getter = vuexClass.Getter;

  static state = vuexClass.State;

  static mutation = vuexClass.Mutation;

  static action = vuexClass.Action;

  static namespace = vuexClass.namespace;

  /**
   *@deprecated :将在2.0版本后续版本中弃用
   */
  readonly httpReqV1: T_httpReqV1 = httpReqV1;

  private TsnowID: ifs.I_snowID;

  // 国际化路径使用记录，通过ws定时传后端，方便记录呆滞时间，以便国际化垃圾回收
  // 值为记录使用日期时间
  private i18nPathUseRec: Map<string, string> = new Map();

  public dialog: ModalController = modal; // 对话

  public TiconsFont: T_iconFont;

  private Ttask: ts.T_task = null;

  private pubApi: ifs.I_eventHandle;

  public TPP: I_personalPreference;

  public wsCommunicate: I_msgHandler;

  // 记录未国际化
  private unGlobalArr: any = [];

  // private messageData: ts.T_wsMsg = new ts.T_wsMsg();

  public TlockInfo: T_lockInfo;

  public TerrDet: T_errDet;

  public eventBus: any;

  constructor() {
    super();
    this.compBox = new ts.T_compBox(this.utils.getGUID());
    this.TsnowID = new ts.T_snowID();
    this.TiconsFont = new T_iconFont();
    this.Ttask = new ts.T_task(10000);
    this.Ttask.addTask(updI18nLastActivityTime);
    this.pubApi = new ts.T_eventHandle();
    this.TlockInfo = new T_lockInfo();
    this.TerrDet = new T_errDet();
    this.TPP = new T_personalPreference(this); // 个人偏好
    this.wsCommunicate = new T_msgHandler(this); // 个人偏好
  }

  // 凡调用《公用API事件》在此执行 【执行"sp" | "ruleLib" | "sysRule"|"svcLogic"】
  public doEvent(
    apiName: string,
    funInfo: ifs.I_funInfo,
    data: () => { [key: string]: any },
    isShowLoading: boolean = true
  ) {
    return this.pubApi
      .setApiName(apiName)
      .doEvent(this.getUserInfo(), funInfo, data, isShowLoading);
  }

  public getIcons(): Set<string> {
    return this.TiconsFont.getIcons();
  }

  public getSnowID(): bigint {
    if (this.TsnowID.snowSeed < 0 || this.TsnowID.snowWorkID < 0) {
      const seed: bigint = this.getSnowIDSeed();
      const workID: bigint = this.getSnowWorkID();
      if (seed < 0 || workID < 0) {
        return -1n;
      }
      this.TsnowID.snowSeed = seed;
      this.TsnowID.snowWorkID = workID;
    }
    return this.TsnowID.getSnowID();
  }

  public getSnowIDFmtStr(): string {
    const id: bigint = this.getSnowID();
    if (id < 0) {
      return '';
    }
    return id.toString();
  }

  // 如果父组件没有传进来就产生一个。应用在vue单元简化代码
  public getCompInfo(parentCompBoxInfo: ifs.I_compBoxInfo): ifs.I_compBoxInfo {
    const isEmpty = this.utils.isEmpty(parentCompBoxInfo);
    if (!isEmpty) {
      return parentCompBoxInfo;
    }
    const compID = this.utils.getGUID();
    return {
      compID,
      TcompBox: new ts.T_compBox(compID)
    };
  }

  /**
   * @description: 通过主功能名获取功能信息
   * @param {string} mainFunName 主功能名
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-20 13:19:07
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getFunInfo(mainFunName: string): ifs.I_funInfo {
    return this.getStore().getters.FunInfo[mainFunName] || null;
  }

  /**
   * @description: 通过功能名查找主功能名信息
   * @param {string} funName 功能名
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-20 13:39:20
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getMainFunInfoByFunName(
    funName: string
  ): { mainFunID: string; mainFun: string } {
    const funInfo = this.getStore().getters.FunInfo;
    if (this.utils.noValue(funInfo)) {
      return null;
    }

    if (!this.utils.noValue(funInfo[funName])) {
      return { mainFun: funName, mainFunID: funInfo[funName].mainFunID };
    }
    for (const k of Object.keys(funInfo)) {
      const inf = funInfo[k]?.deputyFunInfo;
      if (!this.utils.noValue(inf)) {
        for (const ifo of inf) {
          if (ifo.value === funName) {
            return { mainFun: k, mainFunID: funInfo[k].mainFunID };
          }
        }
      }
    }
    return null;
  }

  // 传入主功能名,获取功能id
  public getMinFunID(mainFunName: string): string {
    const m: any = this.getFunInfo(mainFunName);
    return m ? m['mainFunID'] : null;
  }

  // 传入主功能名，副功能名，获取副功能id
  public getDeputyFunID(mainFunName: string, deputyFunName: string): string {
    const _info = this.getFunInfo(mainFunName);
    const arr = _info?.deputyFunInfo.filter(
      item => item.value === deputyFunName
    );
    if (this.utils.noValue(arr)) {
      // <<引用组件>>的副功能是虚拟的，通在在主功能区
      return this.getMinFunID(deputyFunName);
    }
    return arr[0].key;
  }

  /**
   * @description: 通过值获取该语言种类的路径
   *               默认语言为中文简体值,也可以是其它语言值
   * @param {string} val
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-15 12:52:51
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getLangPathByValue(val: string, lang: string = 'zh-CN'): string {
    if (!langContext.has(lang) || this.utils.noValue(val)) {
      return val;
    }
    const context = langContext.get(lang);
    const path = this.utils.findPathFirst(context, val);
    if (path !== '') {
      this.recUseTime(path);
      return path;
    }
    return val;
  }

  /**
   * @description: 根据国际化值获取i18n转换后的当前语言值
   *               1、先根据传入的值获取国际化路径
   *               2、再根据国际化路径调用$t转换当前语言
   * @param {string} val
   * @param {string} lang
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-31 09:55:58
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getI18nByValue(val: string, lang: string = 'zh-CN'): string {
    const path = this.getLangPathByValue(val, lang);
    if (i18n.te(path)) {
      this.recUseTime(path);
      return i18n.t(path).toString();
    }
    return val;
  }

  /**
   * @description: 通过路径获取国际化
   * @param {string} path
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-31 12:21:19
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getI18nByPath(path: string, values?: VueI18n.Values): string {
    if (i18n.te(path)) {
      this.recUseTime(path);
      return i18n.t(path, values).toString();
    }
    return path;
  }

  /**
   * @description: 组建提示消息国际化
   * @param {string} msgCode 提示消息中的消息码
   * @return {*} 返回标准组装的消息体（<code>,FE,<values>,<msg>）
   * @author: wiz
   * @Date: 2025-06-28
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getI18nByMsg(msgCode: string, ...values: string[]): string {
    const _path = `msg.${msgCode}`;
    if (i18n.te(_path)) {
      this.recUseTime(_path);
      if (this.noValue(values)) {
        return `${msgCode},FE,90,xxx`;
      }
      const vf: Array<string> = [];
      values.forEach(v => {
        vf.push(v);
      });
      if (vf.length > 0) {
        return `${msgCode},FE,90,*${vf.join('~|~')}#,xxx`;
      }
      return `${msgCode},FE,90,xxx`;
    }
    console.error('getI18nByMsg:没有国际化内容');
    return msgCode;
  }

  /**
   * @description: 后端国际化值
   *               将传入的key前加i18n.xxx
   *               替换低版本$lang
   * @param {string} key
   * @return {string}
   * @author: wiz
   * @Date: 2023-07-17 07:23:07
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getI18nBySrv(key: string, lang = undefined): string {
    const path: string = `i18n.${key}`;
    if (lang === undefined) {
      if (!i18n.te(path)) {
        return key;
      }
      this.recUseTime(path);
      return i18n.t(path).toString();
    }
    if (!i18n.te(path, lang)) {
      return key;
    }
    this.recUseTime(path);
    return i18n.t(path, lang).toString();
  }

  // 记录使用记录
  private recUseTime(path: string) {
    if (this.utils.noValue(path)) {
      return;
    }
    this.i18nPathUseRec.set(path, this.utils.toDateString(new Date()));
  }

  /**
   * @description: 获取使用最近使用过的I18n记录
   *               垃圾数据回收时通过呆滞日期清理
   *               国际化记录
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-31 12:03:58
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getI18nUseRec(): Map<string, string> {
    return this.i18nPathUseRec;
  }

  /**
   * @description:清理国际化使用记录
   * @return {*}
   * @author: wiz
   * @Date: 2023-07-31 12:07:58
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public clearI18nUseRec() {
    this.i18nPathUseRec.clear();
  }

  /**
   * @description: 开启任务
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-15 09:53:44
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public startTask(): void {
    this.Ttask.startTask();
  }

  /**
   * @description: 停止任务
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-15 09:53:25
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public stopTask(): void {
    this.Ttask.stopTask();
  }

  /**
   * @description: 增加一个事务
   * @param {Function} p
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-15 09:57:19
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public addTask(...p: Function[]): void {
    this.Ttask.addTask(...p);
  }

  /**
   * @description: 删除一个事务
   * @param {Function} p
   * @return {boolean}删除成功返回true,无返回false
   * @author: wiz
   * @Date: 2023-08-15 09:59:56
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public delTask(p: Function): boolean {
    return this.Ttask.delTask(p);
  }

  /**
   * @description: 获取功能配置
   * @param {string} funName 功能名

   * @return {ts.T_return}
   * @author: wiz
   * @Date: 2023-08-20 11:29:32
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getFunConf(funName: string): Promise<ifs.I_return> {
    const getStore = (fuName: string): Promise<any> => {
      return this.getStore().dispatch('getLayoutConfigAsync', {
        funid: null,
        field: fuName,
        type: 'layout'
      });
    };
    const funInfo = this.getMainFunInfoByFunName(funName);
    return new Promise((resolve, reject) => {
      if (funInfo === null) {
        getStore(funName)
          .then(res => {
            if (res) {
              const layoutConfig = this.utils.clone(res.current, true);
              resolve(this.findCompFromLayout(layoutConfig, funName));
            }
          })
          .catch(err => {
            reject(new ts.T_return().error(err.toString()));
          });
        // resolve(
        //   new ts.T_return().error(this.getI18nByValue('没有获取到funInfo'))
        // );
      } else {
        getStore(funInfo.mainFun)
          .then(res => {
            if (res) {
              const layoutConfig = this.utils.clone(res.current, true);
              resolve(this.findCompFromLayout(layoutConfig, funName));
            }
          })
          .catch(err => {
            reject(new ts.T_return().error(err.toString()));
          });
      }
      // this.getStore()
      //   .dispatch('getLayoutConfigAsync', {
      //     funid: null,
      //     field: funInfo.mainFun,
      //     type: 'layout'
      //   })
      //   .then(res => {
      //     if (res) {
      //       const layoutConfig = this.utils.clone(res.current, true);
      //       resolve(this.findCompFromLayout(layoutConfig, funName));
      //     }
      //   })
      //   .catch(err => {
      //     reject(new ts.T_return().error(err.toString()));
      //   });
    });
  }

  /**
   * @description: 从功能配置中layout中查找组件配置
   * @param {any} layoutConf
   * @param {string} compName
   * @return {I_return}
   * @author: wiz
   * @Date: 2023-08-20 12:20:45
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  private findCompFromLayout(layoutConf: any, compName: string): ifs.I_return {
    const noValue: Function = this.utils.noValue;
    let obj: any = {};
    function find(currConf: any, name: string): boolean {
      if (currConf?.name === name) {
        obj = currConf;
        return true;
      }
      if (!noValue(currConf?.first)) {
        if (!noValue(currConf?.first?.config)) {
          if (find(currConf.first.config, name)) {
            return true;
          }
        }
      }
      if (!noValue(currConf?.second)) {
        if (!noValue(currConf?.second?.config)) {
          if (find(currConf.second.config, name)) {
            return true;
          }
        }
      }
      // basePanel tables
      if (
        !noValue(currConf?.tablePanels) &&
        Array.isArray(currConf.tablePanels)
      ) {
        for (const o of currConf.tablePanels) {
          if (o?.content?.name === name) {
            obj = o.content;
            return true;
          }
        }
      }
      // baseTab
      if (!noValue(currConf?.panels && Array.isArray(currConf.panels))) {
        for (const o of currConf.panels) {
          if (o?.content?.config?.name === name) {
            obj = o.content.config;
            return true;
          }
        }
      }
      return false;
    }

    const r = new ts.T_return();
    if (find(layoutConf.config, compName)) {
      r.setReturnData(compName, obj);
    } else {
      r.error('no find');
    }

    return r;
  }

  /**
   * @description: 可靠的发送事件
   *               不同的组件加载时间不同，在加载快的组件向加载慢的组件
   *               发送初始化总线事件时造成初始化失败。
   *               此方法会调用loopExecution循环执行10次间隔100毫秒
   *  @param {string} eventName 事件名
   * @param {any} bus 默认$bus
   * @param {any} bus vue 总线实例
   * @param {any} content 事件内容
   * @param {number} count 循环次数 默认 10次
   * @param {number} interval 循环间隔时间 默认 10毫秒
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-21 14:21:46
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public reliablySendingBusEvent(
    eventName: string,
    bus: any = this.eventBus,
    count: number = 10,
    interval: number = 10,
    ...content: any
  ) {
    if (bus._events[eventName]) {
      bus.$emit(eventName, ...content);
    } else {
      this.loopExecution(
        () => {
          if (bus._events[eventName]) {
            bus.$emit(eventName, ...content);
            return false;
          }
          return true;
        },
        count,
        interval
      );
    }
  }

  /**
   * @description: 循环执行
   * @param {number} callback ()=>boolean 执行回调 如果返回false则停止
   * @param {number} count 循环总次数 默认10次
   * @param {number} interval 间隔时间毫秒 默认100
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-21 14:54:57
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public loopExecution(
    callback: () => boolean,
    count: number = 10,
    interval: number = 100
  ) {
    let cn = 0;
    const timer = setInterval(() => {
      cn++;
      if (cn >= count || !callback()) {
        clearInterval(timer);
      }
    }, interval);
  }

  /**
   * @description: 根据路径获取值
   *               如果值为undefined或者为''|[]|null,则返回devVal或执行devVal
   *               
   * @param {object} o 传入对象
   * @param {string|Array<string>} path 路径 a.b.c
   *                getx({a: {b: 11, c: 22, d: [33, 44]}}, 'a.b') // 11
                    getx({a: {b: 11, c: 22, d: [33, 44]}}, 'a.e', 'default') // 'default'
                    getx({a: {b: 11, c: 22, d: [33, 44]}}, 'a.d[0]') // 33
                    getx({a: {b: 11, c: 22, d: [33, {f: 66}]}}, 'a.d[1].f') // 66
                    getx({a: {b: 11, c: 22, d: [33, 44]}}, ['a', 'c']) // 22 
                    getx({a: {},'a.b',()=>1) // 1 
   * @param {any} defVal 默认值 defVal:object|string|number|null|undefined|function
   * @param {(v)=>void} getVal 获取值()=>any 返回本函数的返回值
   * @return {any}
   * @author: wiz
   * @Date: 2023-08-21 14:54:57
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public getx(
    o: object,
    path: string | Array<string>,
    defVal: any,
    getVal?: (v) => any
  ): any {
    const gv = v => {
      if (typeof getVal === 'function') {
        getVal(v);
      }
    };
    const v = this.utils.get(o, path, '__default__');
    if (v === '__default__' || this.utils.noValue(v)) {
      // if (v === '__default__') {如果返回的是空对象或者空数组也会被当做undefined处理
      if (typeof defVal === 'function') {
        gv(defVal());
        return defVal();
      }
      gv(defVal);
      return defVal;
    }
    gv(v);
    return v;
  }

  /**
   * @description: 发送消息
   * const msg = new T_wsMsg();
   *  msg.setToUserNo('admin'); // "__ALLUSERS__"
      msg.setToClientID(null);
      msg.setCMD(0); // 0：消息、数据采集类、会谈
      msg.setMsgType(0); // 0：广播、预警、警报、通知、待办事项、更新token
      msg.setData('{"123"}');
   * @param {number}
   * @return {*}
   * @author: wiz
   * @Date: 2023-08-21 14:54:57
   * @LastEditors: wiz
   * @LastEditTime: Do not edit
   */
  public sendMsg(msgData: I_wsUserMsg) {
    return new Promise((resolve, reject) => {
      // 发送ws消息
      const msgParams = new T_sendWsMsgParams();
      msgParams.addUserMsgs(msgData);
      // this.web.sendMsg(msgParams);
      // 整理msg数据 （添加到 消息 表格中）
      const message = JSON.parse(msgData.showJsonMsg());
      const msgNewId = this.getSnowIDFmtStr();
      const params = [
        { field: this.globalVar.fieldName.ID, value: msgNewId },
        { field: this.globalVar.fieldName.msgType, value: message.msg.m.t },
        {
          field: this.globalVar.fieldName.msgContent,
          value: JSON.stringify(message.msg.m.d)
        },
        {
          field: this.globalVar.fieldName.msgSender,
          value: this.getStore().getters.UserNo
        },
        {
          field: this.globalVar.fieldName.msgReceiver,
          value: message.toUserNo
        },
        { field: this.globalVar.fieldName.msgStatus, value: 0 }
      ];
      const ctd = new ts.T_ctdArg();
      ctd
        .setTableName(this.globalVar.tableName.SYS_MSG)
        .setDbType(this.globalVar.dbType.MYSQL)
        .setRule({} as ifs.I_rule);
      const scope = {};
      const bDatas: ifs.I_baseData[] = [];
      const bData = new ts.T_baseData();
      bData
        .setDataRule({} as ifs.I_rule)
        .setFileInfo({})
        .setRowData(...(params as ifs.I_fv[]));
      bDatas.push(bData);
      ctd.setInsRecDataMany(scope, ...bDatas);
      this.batRows(ctd).then(resp => {
        const [b, res, msg] = resp;
        msg && Vue.prototype.$msg(msg);
        if (b) {
          this.web.sendMsg(msgParams); // 存表成功后发送ws消息
          resolve(params);
        } else {
          resolve(false);
        }
      });
    });
  }

  /**
   * @description: 自定义提示消息，用于数据库报错信息
   * this.$msg("mysql-1062,DB,90,Duplicate entry 'field'for key 'uNIQuE capacity set 1")
   * @param {string} s
   * @param {array} vals
   * @return {*}
   * @author: zss
   * @Date: 2024-06-24 17:12:16
   * @LastEditors: zss
   * @LastEditTime: Do not edit
   */
  public customMsgHandle(s: string, ...vals: string[]): string[] {
    let stringVals: string[] = [];
    const arr = s.split(',');
    switch (arr[0]) {
      case 'mysql-1062':
        stringVals.push(this.getI18nBySrv(/'([^']+)'/.exec(arr[3])[1]));
        break;
      case 'mysql-1364':
        stringVals.push(this.getI18nBySrv(/'([^']+)'/.exec(arr[3])[1]));
        break;
      default:
        stringVals = vals;
        break;
    }
    return stringVals;
  }

  // 中转 在代码中写Tsrv.utils.noValue()过长
  public noValue(p: any): boolean {
    return this.utils.noValue(p);
  }

  // 批量判断
  public noValues(...ps: any[]): boolean {
    if (!this.noValue(ps)) {
      if (Array.isArray(ps)) {
        for (const v of ps) {
          if (this.noValue(v)) {
            return true;
          }
        }
      }
      return false;
    }
    return false;
  }

  /**
   * @description: 送审流程
   * @param {string} funID 功能id
   * @param {string} title 审批标题
   * @param {string} approvalData 审批数据
   * @return {*}
   * @author: swc
   * @Date: 2024-09-02 16:54:21
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  public requestAudit(
    funID: string,
    title: string,
    approvalData: string
  ): Promise<boolean> {
    // 执行操作：查找bpmn库中是否有该表的审批流程逻辑，有则取已审核的且最高版本审批流程逻辑创建内部联络单，同时补充对应的内部联络单明细和日志
    return new Promise((resolve, reject) => {
      const gpd = new ts.T_getPartDataArg();
      gpd
        .setFunName(this.globalVar.tableName.BPMN_LIB)
        .setLang(this.store.state.App.language);
      gpd.setPage(1, 99999).setDbType(this.globalVar.dbType.MYSQL);
      gpd.setFilter({
        $and: [
          {
            _sys_fun_id: {
              $in: [funID]
            }
          },
          {
            _audited_: {
              $in: [1]
            }
          }
        ]
      });
      this.getData<any, ifs.I_getPartDataArg>(gpd)
        .then(([b, res, rows, msg]) => {
          if (!b) {
            msg && Vue.prototype.$Message.info(msg);
            reject(false);
          }
          if (rows.length > 0) {
            const bpmn = rows.reduce((max, item) => {
              return max.ver_num > item.ver_num ? max : item;
            }, rows[0]);
            this.addLiaison(title, approvalData, bpmn).then(r => {
              if (r) resolve(true);
            });
          } else {
            Vue.prototype.$Message.info('未找到对应流程图');
            resolve(false);
          }
        })
        .catch(er => {
          console.error('获取内部联络单失败', er);
        });
    });
  }

  private addLiaison(
    title: string,
    approvalData: string,
    bpmn: any
  ): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const liaisonID = this.getSnowIDFmtStr();
      const data: Array<ifs.I_fv> = [
        new ts.T_fv().setField(this.globalVar.fieldName.ID).setValue(liaisonID),
        new ts.T_fv().setField(this.globalVar.fieldName.title).setValue(title),
        // 后期注释不要，之后会用自动编号实现
        new ts.T_fv().setField('f_type').setValue(0),
        new ts.T_fv().setField('f_status').setValue(0),
        new ts.T_fv().setField('_sys_graph_id').setValue(bpmn.id),
        new ts.T_fv().setField('f_data').setValue(approvalData)
      ];
      const ins = new ts.T_ctdInsRec();
      ins.setScope({}).setBaseDataGeneral({} as ifs.I_rule, {}, ...data);
      this.addRows(
        this.globalVar.tableName.SYS_INTERNAL_LIAISON_SHEET,
        this.globalVar.dbType.MYSQL,
        {} as ifs.I_rule,
        ins
      )
        .then(([ok, res, err]) => {
          if (!ok || !err) {
            Vue.prototype.$msg(err ?? res.msg);
            reject(false);
          } else {
            // that.$msg(res.msg);
            this.addLiaisonLog(liaisonID, 0, '新建内部联络单');
            const masterInfo = {
              masterID: liaisonID
            };
            const mess = new ts.T_msgToDoList()
              .setCategory(tvs.V_todoListCategory.APPROVAL)
              .setLiaisonID(liaisonID)
              .setContent(`${title}-数据审批`)
              .setTitle(title);
            this.addLialisonSheetDet(masterInfo, bpmn, mess).then(r => {
              if (r) {
                resolve(true);
              }
            });
          }
        })
        .catch(er => {
          console.error(er);
        });
    });
  }

  // 添加内部联络单明细表
  private addLialisonSheetDet(
    masterInfo: any,
    bpmn: any,
    message: any
  ): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const nodeDSL = JSON.parse(bpmn?.dsl_code);
      if (this.noValues(masterInfo.masterID, nodeDSL)) {
        Vue.prototype.$msg(this.getI18nByValue('内部联络单主单ID为空'));
        reject(false);
        return;
      }
      const insertRecords = [];
      let firstNdoe = {};
      nodeDSL.forEach(ele => {
        const id = this.getSnowIDFmtStr();
        const insert: Array<ifs.I_fv> = [
          new ts.T_fv().setField(this.globalVar.fieldName.ID).setValue(id),
          new ts.T_fv()
            .setField(this.globalVar.fieldName.masterBillId)
            .setValue(masterInfo.masterID),
          new ts.T_fv()
            .setField(this.globalVar.fieldName.fCode)
            .setValue(ele.nodeCode),
          new ts.T_fv().setField('sn').setValue(0),
          new ts.T_fv().setField('f_type').setValue(ele.type),
          new ts.T_fv()
            .setField('confirm')
            .setValue(ele.previous.length === 0 && ele.type === 1 ? 1 : 0),
          new ts.T_fv()
            .setField('son_data')
            .setValue(JSON.stringify(ele.subsequent)),
          // new ts.T_fv().setField('content').setValue(JSON.stringify({})),
          new ts.T_fv()
            .setField('father_data')
            .setValue(JSON.stringify(ele.previous)),
          new ts.T_fv()
            .setField('graph_data')
            .setValue(JSON.stringify(ele.data))
        ];
        if (ele.previous.length === 0) {
          // insert.forEach(el => {
          //   firstNdoe[el.field] = el.value;
          // });
          firstNdoe = {
            id,
            type: ele.type,
            nodeID: ele.nodeCode,
            nodeData: ele.data,
            previousNodes: ele.previous,
            subsequentNodes: ele.subsequent
          };
        }
        insertRecords.push(insert);
      });
      // 增加数据
      const ctd = new ts.T_ctdArg();
      ctd
        .setTableName(this.globalVar.tableName.SYS_INTERNAL_LIAISON_SHEET_DET)
        .setDbType(this.globalVar.dbType.MYSQL)
        .setRule({} as ifs.I_rule);
      const scope = {};
      const bDatas: ifs.I_baseData[] = [];
      insertRecords.forEach(ele => {
        const bData = new ts.T_baseData();
        bData
          .setDataRule({} as ifs.I_rule)
          .setFileInfo({})
          .setRowData(...(ele as ifs.I_fv[]));
        bDatas.push(bData);
      });
      ctd.setInsRecDataMany(scope, ...bDatas);
      this.batRows(ctd).then(resp => {
        const [b, res, msg] = resp;
        // msg && this.$msg(msg);
        if (b) {
          const approvalBasicData = {
            funName: bpmn['m2o|_sys_fun_id|_sys_fun|title'],
            liasionID: masterInfo.masterID,
            graphID: bpmn,
            previousNodes: [],
            subsequentNodes: []
          };
          this.sendAuditMsg(firstNdoe, approvalBasicData, message);
          resolve(true);
        } else {
          Vue.prototype.$msg(this.getI18nByValue('添加内部联络单明细表失败'));
          reject();
        }
      });
    });
  }

  /**
   * @description: 添加内部联络单日志
   * @param {string} id: 内部联络单id
   * @param {number} type: 0-收到消息 1-查看内容 2-批注 3-确认 4-驳回
   * @param {string} info: 备注
   * @return {*}
   * @author: swc
   * @Date: 2024-09-04 19:29:50
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  public addLiaisonLog(
    id: string,
    type: number,
    info: string
  ): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const data: Array<ifs.I_fv> = [
        new ts.T_fv()
          .setField(this.globalVar.fieldName.ID)
          .setValue(this.getSnowIDFmtStr()),
        new ts.T_fv()
          .setField(this.globalVar.fieldName.masterBillId)
          .setValue(id),
        new ts.T_fv().setField('f_type').setValue(type),
        new ts.T_fv().setField('f_comment').setValue(info)
      ];
      const ins = new ts.T_ctdInsRec();
      ins.setScope({}).setBaseDataGeneral({} as ifs.I_rule, {}, ...data);
      this.addRows(
        this.globalVar.tableName.SYS_INTERNAL_LIAISON_SHEET_LOG,
        this.globalVar.dbType.MYSQL,
        {} as ifs.I_rule,
        ins
      )
        .then(([ok, res, err]) => {
          if (!ok || !err) {
            Vue.prototype.$msg(err ?? res.msg);
            reject();
          } else {
            Vue.prototype.$msg(res.msg);
            resolve(true);
          }
        })
        .catch(er => {
          console.error(er);
        });
    });
  }

  private sendAuditMsg(nodes: any, basicData: any, mess: any) {
    if (Array.isArray(nodes)) {
      const ctd = new ts.T_ctdArg();
      ctd
        .setTableName(this.globalVar.tableName.SYS_INTERNAL_LIAISON_SHEET_DET)
        .setDbType(this.globalVar.dbType.MYSQL);
      ctd.setRule({} as ifs.I_rule);
      let filter: any = {};
      const ctdUpdDatas: ifs.I_ctdUpdData[] = [];
      nodes.forEach(ele => {
        const ctdUpdData = new ts.T_ctdUpdData();
        ctdUpdData.setDataRule({} as ifs.I_rule).setFileInfo({});
        const rowData: ifs.I_fv[] = [];

        filter = { id: ele.id };
        rowData.push(
          new ts.T_fv().setField('sn').setValue(ele.type === 0 ? 1 : 0)
        );
        ctdUpdData.setFilter(filter);
        ctdUpdData.setRowData(...rowData);
        ctdUpdDatas.push(ctdUpdData);
      });
      ctd.setUpdRecDataMany(...ctdUpdDatas);
      this.batRows(ctd).then(resp => {
        const [b, res, msg] = resp;
        msg && Vue.prototype.$msg(msg);
        if (b) {
          // resolve(true);
          const approvers = nodes.filter((item: any) => item.type === 0);
          const nextApprovers = approvers.map((itm: any) => {
            return itm?.nodeData?.checker;
          });
          nextApprovers.forEach((ele: any) => {
            const message = new ts.T_wsMsg();
            message
              .setToUserNo(ele.value)
              .setToClientID(null)
              .setCMD(0)
              .setMsgType(4)
              .setData(mess);
            this.sendMsg(message);
          });
        } else {
          // reject();
        }
      });
    } else {
      if (nodes.type === 0) {
        this.sendAuditMsg([nodes], basicData, mess);
      }
      if (nodes.type === 1) {
        if (this.noValue(nodes?.nodeData)) {
          return;
        }
        const nodeData = JSON.parse(nodes.nodeData);
        if (this.noValue(nodeData?.condition?.value)) {
          return;
        }
        const condition = nodeData?.condition?.value;
        basicData['id'] = nodes?.id;
        basicData['nodeID'] = nodes?.nodeID;
        basicData.previousNodes = JSON.parse(nodes.previousNodes);
        basicData.subsequentNodes = JSON.parse(nodes.subsequentNodes);
        this.doEvent(
          condition,
          this.getFunInfo(basicData.funName) as ifs.I_funInfo,
          () => basicData
        )
          .then(r => {
            if (r.isErr()) {
              Vue.prototype.$msg(r.errmsg);
              // resolve(false);
            } else {
              const res = r.getReturnData('conf');
              const data = res?.res?.data?.request;
              if (this.noValues(data, data?.nextApprovers)) {
                Vue.prototype.$msg(
                  this.getI18nByValue('没有获取到下一级审批人')
                );
                return;
              }
              const nextApprovers = [...data.nextApprovers, nodes];
              this.sendAuditMsg(nextApprovers, basicData, mess);
              // resolve([true, data.nextApprovers]);
            }
          })
          .catch(err => {
            Vue.prototype.$msg(err);
            // reject(false);
          });
      }
    }
  }

  public readPlcData(
    codes: string[]
  ): Promise<ifs.I_scadaReadResp['data'] | boolean> {
    return this.standardPostReq<ifs.I_scadaReadResp, any>(
      { ecDetCodes: codes },
      this.url.scadaRead
    )
      .then(([readResp, err]) => {
        if (!this.noValue(err) || !readResp.success) {
          Vue.prototype.$msg(readResp.msg);
          return false;
        }
        return readResp.data;
      })
      .catch(err => {
        Vue.prototype.$msg(err);
        return false;
      });
  }

  public writePlcData(data: ifs.I_scadaWriteCodeRes[]): Promise<boolean> {
    return this.standardPostReq<ifs.I_scadaWriteResp, ifs.I_scadaWriteRes>(
      { writeList: data },
      this.url.scadaWrite
    )
      .then(([res, err]) => {
        if (err || !res.success) {
          Vue.prototype.$msg(res.msg);
          return false;
        }
        return true;
      })
      .catch(err => {
        Vue.prototype.$msg(err);
        return false;
      });
  }

  public getQueryData(p: ifs.I_qryDataReq) {
    return this.standardPostReq<ifs.I_qryDataResp, ifs.I_qryDataReq>(
      p,
      this.url.scadaQueryData
    ).then(([resp, err]) => {
      if (err || !resp.success) {
        Vue.prototype.$msg(resp.msg);
        return false;
      }
      const data = JSON.parse(wUnZipStr(resp.data));
      return data;
    });
  }
}
interface I_srv extends T_srv {}
const Tsrv = new T_srv();

const vue = vuePropertyDecorator.Vue;
export {
  I_srv,
  ifs,
  T_srv,
  // 全局类
  ts,
  // T_srv 实例
  Tsrv,
  // 全局常量、枚举定义
  tvs,
  types,
  vue
};
